<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Redis</title>
    <meta name="generator" content="VuePress 1.5.2">
    <link rel="apple-touch-icon" href="/blog/apple-touch-icon.png">
    <link rel="icon" href="/blog/favicon.ico">
    <link rel="manifest" href="/blog/manifest.json">
    <link rel="mask-icon" href="/blog/icons/icon.svg" color="#ffffff">
    <meta name="description" content="可能是全网最给力的前端博客">
    <meta name="theme-color" content="#ffffff">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="msapplication-TileImage" content="/icons/icon-144x144.png">
    <link rel="preload" href="/blog/assets/css/0.styles.3f022aa2.css" as="style"><link rel="preload" href="/blog/assets/js/app.7fa07907.js" as="script"><link rel="preload" href="/blog/assets/js/2.b6c629b5.js" as="script"><link rel="preload" href="/blog/assets/js/29.0ee8fc42.js" as="script"><link rel="preload" href="/blog/assets/js/3.589a64d8.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.8e59817e.js"><link rel="prefetch" href="/blog/assets/js/11.34cd2f44.js"><link rel="prefetch" href="/blog/assets/js/12.445d88fd.js"><link rel="prefetch" href="/blog/assets/js/13.e6460b96.js"><link rel="prefetch" href="/blog/assets/js/14.d0ee0fcf.js"><link rel="prefetch" href="/blog/assets/js/15.e91e78ed.js"><link rel="prefetch" href="/blog/assets/js/16.8310de97.js"><link rel="prefetch" href="/blog/assets/js/17.6ca97902.js"><link rel="prefetch" href="/blog/assets/js/18.a82e534b.js"><link rel="prefetch" href="/blog/assets/js/19.e3a914ee.js"><link rel="prefetch" href="/blog/assets/js/20.6db77b80.js"><link rel="prefetch" href="/blog/assets/js/21.e705be23.js"><link rel="prefetch" href="/blog/assets/js/22.efc39414.js"><link rel="prefetch" href="/blog/assets/js/23.5f4c7658.js"><link rel="prefetch" href="/blog/assets/js/24.359a62e4.js"><link rel="prefetch" href="/blog/assets/js/25.c49ef7a9.js"><link rel="prefetch" href="/blog/assets/js/26.8d45691f.js"><link rel="prefetch" href="/blog/assets/js/27.364b1802.js"><link rel="prefetch" href="/blog/assets/js/28.485228c0.js"><link rel="prefetch" href="/blog/assets/js/30.465920a7.js"><link rel="prefetch" href="/blog/assets/js/4.1dbf8b08.js"><link rel="prefetch" href="/blog/assets/js/5.9253897f.js"><link rel="prefetch" href="/blog/assets/js/6.4c1f13e3.js"><link rel="prefetch" href="/blog/assets/js/7.bc1839d0.js"><link rel="prefetch" href="/blog/assets/js/8.611f5ec5.js"><link rel="prefetch" href="/blog/assets/js/9.5813bb74.js">
    <link rel="stylesheet" href="/blog/assets/css/0.styles.3f022aa2.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><img src="/blog/app.png" alt="卢本伟广场" class="logo"> <span class="site-name can-hide">卢本伟广场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/dsm/" class="nav-link">
  歪比歪比
</a></div><div class="nav-item"><a href="/blog/lbw/" class="nav-link router-link-active">
  歪比吧卜
</a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/blog/dsm/" class="nav-link">
  歪比歪比
</a></div><div class="nav-item"><a href="/blog/lbw/" class="nav-link router-link-active">
  歪比吧卜
</a></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/blog/lbw/" aria-current="page" class="sidebar-link">后端技术总结</a></li><li><a href="/blog/lbw/redis.html" aria-current="page" class="active sidebar-link">Redis</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#计算机存储器" class="sidebar-link">计算机存储器</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#为什么读mysql数据库数据比读文件数据快" class="sidebar-link">为什么读Mysql数据库数据比读文件数据快</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#redis是什么" class="sidebar-link">Redis是什么</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#为什么需要redis缓存数据" class="sidebar-link">为什么需要Redis缓存数据</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#为什么只用redis做缓存不做主数据库" class="sidebar-link">为什么只用Redis做缓存不做主数据库</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#为什么不用map替代redis" class="sidebar-link">为什么不用Map替代Redis</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#怎么保证数据库与缓存数据的一致性" class="sidebar-link">怎么保证数据库与缓存数据的一致性</a></li><li class="sidebar-sub-header"><a href="/blog/lbw/redis.html#redis是单线程还是多线程" class="sidebar-link">Redis是单线程还是多线程</a></li></ul></li><li><a href="/blog/lbw/mq.html" class="sidebar-link">消息队列</a></li><li><a href="/blog/lbw/job.html" class="sidebar-link">XXL-JOB</a></li><li><a href="/blog/lbw/es.html" class="sidebar-link">ElasticSearch</a></li><li><a href="/blog/lbw/concurrent.html" class="sidebar-link">高并发编程</a></li><li><a href="/blog/lbw/ood.html" class="sidebar-link">面向对象编程</a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="redis"><a href="#redis" class="header-anchor">#</a> Redis</h1> <h2 id="计算机存储器"><a href="#计算机存储器" class="header-anchor">#</a> 计算机存储器</h2> <p><strong>外存储器</strong>（<strong>价格便宜</strong>、容量大、<strong>读取速度慢（毫秒）</strong>、断电后数据不会丢失）：存放暂时不用的程序和数据，通常是磁性介质，像硬盘、软盘、光盘、U盘、磁带等</p> <p><strong>内存储器</strong>（<strong>价格贵</strong>、容量小、<strong>读取速度快（纳秒）</strong>、断电后数据会丢失）：存放立即要用的程序和数据，是外存与CPU进行沟通的桥梁，用于暂时存放CPU中的运算数据，与硬盘等外部存储器交换的数据</p> <p>电脑程序一般安装在硬盘等外存上，运行时操作系统会把程序调入内存中运行，当我们使用Word处理文档，在键盘上敲入字符时，它被存入内存中；当选择保存时，内存中的数据才会被存入硬盘</p> <blockquote><p><strong>如果全部数据存在硬盘上，随着文件变大，文件全量IO扫描速度会越来越慢</strong></p></blockquote> <h2 id="为什么读mysql数据库数据比读文件数据快"><a href="#为什么读mysql数据库数据比读文件数据快" class="header-anchor">#</a> 为什么读Mysql数据库数据比读文件数据快</h2> <p>分治：数据文件中的数据，被格式化成一块块的数据页（data page）保存在硬盘中</p> <p>索引：在内存中使用B+树，索引数据页数据，命中索引后把数据页从硬盘调到内存读取数据</p> <h2 id="redis是什么"><a href="#redis是什么" class="header-anchor">#</a> Redis是什么</h2> <p>Redis是一种key-value结构的非关系型数据库，因为基于缓存所以读写速度非常快【纳秒级】，同时key有着过期淘汰策略，value还支持多种数据类型，Redis还拥有发布订阅和等特性，在分布式系统中常常被当作缓存中间件来使用</p> <h2 id="为什么需要redis缓存数据"><a href="#为什么需要redis缓存数据" class="header-anchor">#</a> 为什么需要Redis缓存数据</h2> <ul><li><strong>加快接口响应速度</strong>：从内存（Redis）读数据比从硬盘（数据库）上读数据快，如果碰到执行耗时久且不频繁变动的SQL，就特别适合将运行结果（<strong>热点数据</strong>）放入Redis，后面的请求就去缓存中取，使请求能够迅速响应</li> <li><strong>减轻数据库高并发负担</strong>：使用Redis做缓冲，让接口先访问Redis，如果没命中缓存再去读数据库，防止数据库因高并发出现的连接异常问题</li></ul> <h2 id="为什么只用redis做缓存不做主数据库"><a href="#为什么只用redis做缓存不做主数据库" class="header-anchor">#</a> 为什么只用Redis做缓存不做主数据库</h2> <ul><li>内存昂贵且有限，无法大量存储数据</li> <li>由于断电内存数据会丢失，无法保证数据安全</li> <li>key-value结构无法储存表之间有强关联的数据</li> <li>没有类似SQL的查询引擎，无法完成数据关联查询</li></ul> <h2 id="为什么不用map替代redis"><a href="#为什么不用map替代redis" class="header-anchor">#</a> 为什么不用Map替代Redis</h2> <ul><li>Redis是分布式缓存，数据可以持久化保存；Map是本地缓存，每次项目重启Map数据会被释放掉，不能被其他进程共享，很难保证数据一致性</li> <li>Redis有缓存过期机制而Map没有</li> <li>Redis能使用的内存（几十G）比JVM分配给Map的内存（几G）多得多</li></ul> <h2 id="怎么保证数据库与缓存数据的一致性"><a href="#怎么保证数据库与缓存数据的一致性" class="header-anchor">#</a> 怎么保证数据库与缓存数据的一致性</h2> <p>对于读操作：</p> <ul><li>读缓存</li> <li>命中就返回缓存数据</li> <li>没有命中就读数据库，更新缓存</li></ul> <p>而对于更新操作有两种情况：</p> <ul><li>先更新数据库，再操作缓存（delete/set）</li> <li>先操作缓存（delete/set），再更新数据库</li></ul> <h3>从原子性角度来讲</h3> <p>如果第一步失败可以返回50x，第二步根本不会执行但如果是第一步成功，第二步失败；而如果是第一步成功，第二步失败时</p> <p><strong>先更新数据库，再操作缓存（delete/set）：</strong> 数据库里是新数据，而缓存里是旧数据，业务上无法接受，因为数据应该以数据库数据为准</p> <p><strong>先操作缓存（delete/set），再更新数据库：</strong></p> <ul><li>delete操作成功，数据库更新数据失败：缓存里没有数据，会去数据库找旧数据</li> <li>set操作成功，数据库更新数据失败：缓存和数据库数据会不一致，所以不考虑</li></ul> <h3>从线程安全角度来讲</h3> <p><strong>先更新数据库，再操作缓存（delete/set）：</strong></p> <p>当对缓存的操作是更新时</p> <ul><li>线程A更新数据库</li> <li>线程B更新数据库</li> <li>线程B更新缓存</li> <li>线程A更新缓存</li></ul> <p>此时缓存中存在脏数据，所以不考虑</p> <p>当对缓存的操作是删除时</p> <ul><li>线程A查询数据库，得到旧值</li> <li>线程B更新数据库</li> <li>线程B删除缓存</li> <li>线程A更新缓存</li></ul> <p>上述情况很难出现，</p> <p>因为更新数据库比读数据库操作要慢，</p> <p>所以【线程B删缓存】要领先于【线程A更新缓存】概率特别低，但可以优化</p> <p><strong>先操作缓存（delete/set），再更新数据库：</strong></p> <p>当对缓存的操作是删除时</p> <ul><li>线程A删除了缓存</li> <li>线程B去读数据库，得到旧值</li> <li>线程B将旧值写入缓存</li> <li>线程A将新值写入数据库</li></ul> <p>此时出现了数据库和缓存不一致的问题，但可以优化</p> <p>当对缓存的操作是更新时</p> <ul><li>线程A更新了缓存</li> <li>线程B更新了缓存</li> <li>线程B更新了数据库</li> <li>线程A更新了数据库</li></ul> <p>此时缓存中存在脏数据，所以不考虑</p> <p><strong>因为更新缓存更容易造成数据不一致所以对于更新操作，应该淘汰缓存而不是更新缓存</strong></p> <ul><li>【先更新数据库，再淘汰缓存】在破坏原子性时表现优异</li> <li>【先淘汰缓存，再更新数据库】在高并发场景下表现优异</li></ul> <p><strong>优化手段：</strong></p> <ul><li>延时双删（同步删+重复异步删）</li> <li>databus（消息队列串行化）</li> <li>canal配合binlog（消息队列串行化）</li></ul> <h2 id="redis是单线程还是多线程"><a href="#redis是单线程还是多线程" class="header-anchor">#</a> Redis是单线程还是多线程</h2> <p>计算机执行程序时主要涉及到两个操作，分别是读写操作和计算操作</p> <ul><li>读写操作主要涉及到网络IO和磁盘IO</li> <li>计算操作主要涉及到CPU</li></ul> <p>Redis 6.0之前</p> <ul><li>网络IO模块和数据读写模块是在同一个线程进行</li> <li>持久化储存模块和集群支持模块等其他模块是多线程的</li></ul> <img src="/blog/images/IMG_8500.png" class="emo"> <p>由于Redis本身是基于内存操作，计算效率是纳秒级，所以不用引入多线程来提升Redis的CPU利用率；而Redis确实是属于IO密集型服务，确实需要提升IO的利用率，但这并不意味着只能引入多线程技术才能做到，因为多线程会带来很多复杂的并发问题，维护成本，和线程切换带来的性能开销，所以<strong>在 6.0 之前 Redis 选择了单线程多路复用IO技术来提升IO利用率</strong></p> <p>而随着越来越复杂的业务员场景，为了支持更高的QPS而不去浪费Redis机器资源，2020年5月份，Redis正式推出了6.0版本，这个版本中有很多重要的新特性，其中的多线程特性就是用来突破网络IO瓶颈，毕竟多路复用的IO模型本质上仍然是同步阻塞型IO模型</p> <img src="/blog/images/IMG_8501.png" class="emo"> <blockquote><p><strong>多线程使得网络处理的请求并发进行，大大的提升性能，除了可以减少由于网络 I/O 等待造成的影响，还可以充分利用 CPU 的多核优势，另外数据的执行还是单线程，所以没有线程安全问题</strong></p></blockquote></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">上次更新:</span> <span class="time">4/27/2021, 5:50:04 PM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/blog/lbw/" class="prev router-link-active">
        后端技术总结
      </a></span> <span class="next"><a href="/blog/lbw/mq.html">
        消息队列
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"><BackToTop></BackToTop><!----><Live2D></Live2D></div></div>
    <script src="/blog/assets/js/app.7fa07907.js" defer></script><script src="/blog/assets/js/2.b6c629b5.js" defer></script><script src="/blog/assets/js/29.0ee8fc42.js" defer></script><script src="/blog/assets/js/3.589a64d8.js" defer></script>
  </body>
</html>
